package com.zengdada.authority.utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.Security;

/**
 * @author  12614
 * @contact  1261438511@qq.com
 * @date  2018/10/31 12:38
 * @description  常用加密工具类
 */
public class HEXUtil {
    // 缓存最后一次 Cipher （业务上一般都是不变的 ）
    private static String CACHE_PRE_CIPHER_HEX = "";
    private static Cipher CACHE_PRE_CIPHER = null;

    public enum Type {
        MD5("MD5"), SHA1("SHA-1"), SHA512("SHA-512"), SHA256("SHA-256");
        private String type;
        private String charSet = "UTF-8";

        Type(String type) {
            this.type = type;
        }

        public void setCharSet(String charSet) {
            this.charSet = charSet;
        }
    }

    public enum CipherType {
        AES128("AES", "AES", 128),//, AES192("AES", 192), AES256("AES", 256);
        AES_ECB_PKCS5("AES", "AES/ECB/PKCS5Padding", 128),
        AES_ECB_PKCS7("AES", "AES/ECB/PKCS7Padding", 128);
        private String algorithm;
        private String algorithmProvider;
        private String charSet = "UTF-8";
        private int len = 128;
        // 是否使用密码作为种子生成 SecretKeySpec 否则直接创建
        private boolean isRandomKey = true;


        CipherType(String algorithm, String algorithmProvider, int len) {
            this.algorithm = algorithm;
            this.algorithmProvider = algorithmProvider;
            this.len = len;
        }

        public void setCharSet(String charSet) {
            this.charSet = charSet;
        }

        public void setLen(int len) {
            this.len = len;
        }

        public void setRandomKey(boolean randomKey) {
            isRandomKey = randomKey;
        }

        @Override
        public String toString() {
            return "CipherType{" +
                    "algorithm='" + algorithm + '\'' +
                    ", algorithmProvider='" + algorithmProvider + '\'' +
                    ", charSet='" + charSet + '\'' +
                    ", len=" + len +
                    ", isRandomKey=" + isRandomKey +
                    '}';
        }
    }

    /**
     * 获取加密后的字符串 不可逆
     *
     * @param pw   明文
     * @param type 加密类型
     * @return
     */
    public static String stringHEX(String pw, Type type) {
        if (pw == null) {
            return null;
        }
        try {
            // 拿到一个MD5转换器（如果想要SHA1参数换成”SHA1”）
            MessageDigest messageDigest = MessageDigest.getInstance(type.type);
            // 输入的字符串转换成字节数组
            byte[] inputByteArray = pw.getBytes(type.charSet);
            // inputByteArray是输入字符串转换得到的字节数组
            messageDigest.update(inputByteArray);
            // 转换并返回结果，也是字节数组，包含16个元素
            byte[] resultByteArray = messageDigest.digest();
            // 字符数组转换成字符串返回
            return byteArrayToHex(resultByteArray);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 加密 可解密
     *
     * @param c    明文
     * @param p    密码
     * @param type 类型
     * @return 加密后并Hex后的字符串
     */
    public static String encrypt(String c, String p, CipherType type) {
        try {
            byte[] byteArray = encrypt2Byte(c, p, type);
            if (byteArray == null) {
                return null;
            }
            return byteArrayToHex(byteArray);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 加密 可解密
     *
     * @param c    明文
     * @param p    密码
     * @param type 类型
     * @return 加密后字节数组
     */
    public static byte[] encrypt2Byte(String c, String p, CipherType type) {
        try {
            Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, p, type);
            byte[] byteContent = c.getBytes(type.charSet);
            return cipher.doFinal(byteContent);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解密
     *
     * @param m    密文
     * @param p    密码
     * @param type 类型
     * @return 解密后的字符串
     */
    public static String decrypt(String m, String p, CipherType type) {
        try {
            //执行操作
            byte[] result = decryptToByte(hexToByteArray(m), p, type);
            if (result == null) {
                return null;
            }
            return new String(result, type.charSet);
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 解密
     *
     * @param m    密文
     * @param p    密码
     * @param type 类型
     * @return 解密后的字符串
     */
    public static String decrypt(byte[] m, String p, CipherType type) {
        try {
            byte[] bytes = decryptToByte(m, p, type);
            if (bytes == null) {
                return null;
            }
            return new String(bytes, type.charSet);
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 解密
     *
     * @param m    密文
     * @param p    密码
     * @param type 类型
     * @return 解密后的字符串
     */
    public static byte[] decryptToByte(byte[] m, String p, CipherType type) {
        try {
            Cipher cipher = createCipher(Cipher.DECRYPT_MODE, p, type);
            //执行操作
            return cipher.doFinal(m);
        } catch (Exception ex) {
            return null;
        }
    }

    private static Cipher createCipher(int decryptMode, String p, CipherType type) throws Exception {
        String hex = stringHEX(type.toString() + "[" + decryptMode + "][" + p + "]", Type.MD5);
        if (!"".equals(CACHE_PRE_CIPHER_HEX) && CACHE_PRE_CIPHER_HEX.equals(hex) && CACHE_PRE_CIPHER != null) {
            return CACHE_PRE_CIPHER;
        }
        if (type.algorithmProvider.contains("PKCS7Padding")) {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        }
        //实例化
        Cipher cipher = Cipher.getInstance(type.algorithmProvider);
        //使用密钥初始化，设置为解密模式
        cipher.init(decryptMode, getSecretKey(p, type));
        CACHE_PRE_CIPHER = cipher;
        CACHE_PRE_CIPHER_HEX = hex;
        return cipher;
    }

    private static SecretKeySpec getSecretKey(final String p, CipherType type) throws Exception {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        // 是否需要把密码作为种子
        if (!type.isRandomKey) {
            return new SecretKeySpec(p.getBytes(), type.algorithm);
        }
        KeyGenerator kg = KeyGenerator.getInstance(type.algorithm);
        //AES 要求密钥长度为 len
        kg.init(type.len, new SecureRandom(p.getBytes()));
        //生成一个密钥
        SecretKey secretKey = kg.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), type.algorithm);// 转换为AES专用密钥

    }

    public static String byteArrayToHex(byte[] byteArray) {
        // 首先初始化一个字符数组，用来存放每个16进制字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        // new一个字符数组，这个就是用来组成结果字符串的（解释一下：一个byte是八位二进制，也就是2位十六进制字符（2的8次方等于16的2次方））
        char[] resultCharArray = new char[byteArray.length * 2];
        // 遍历字节数组，通过位运算（位运算效率高），转换成字符放到字符数组中去
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        // 字符数组组合成字符串返回
        return new String(resultCharArray);
    }

    public static byte[] hexToByteArray(String hexString) {
        byte[] result = new byte[hexString.length() / 2];
        for (int i = 0; i < result.length; i++) {
            result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
        }
        return result;
    }

    /**
     * 可逆的加密算法 一次加密 两次解密
     *
     * @param inStr
     * @return
     */
    public static String convertHex(String inStr) {
        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        return new String(a);
    }

}